﻿using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.IsolatedStorage;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using Microsoft.Phone.Tasks;

// Originally From http://blogs.msdn.com/b/andypennell/archive/2010/11/01/error-reporting-on-windows-phone-7.aspx
// Also taken improvements from Jeff Wilcox's version at https://github.com/jeffwilcox/thejeffwilcox/tree/master/src/Shared/Platform

namespace Phoenix.Diagnostics
{
    // ReSharper disable EmptyGeneralCatchClause
    public class LittleWatson : IErrorReporter
    {
        const string Filename = "LittleWatson.txt";
        const string NeverFilename = "IgnoreLittleWatson.txt";

        public void ReportException(Exception ex, string extra, bool fatalError)
        {
            if (fatalError)
            {
                try
                {
                    using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                    {
                        SafeDeleteFile(store);
                        using (var output = new StreamWriter(store.CreateFile(Filename)))
                        {
                            WriteError(ex, extra, output);
                        }
                    }
                }
                catch (Exception)
                {
                }
            }
            else
            {
                using (var stringWriter = new StringWriter())
                {
                    WriteError(ex, extra, stringWriter);
                    ReportError(stringWriter.ToString());
                }
            }
        }

        static void WriteError(Exception ex, string extra, TextWriter output)
        {
            var appInfo = Application.Current as IAppInfo;
            output.WriteLine(appInfo == null ? "Unknown version" : appInfo.Version);

            output.WriteLine(extra);
            output.WriteLine();
            output.WriteLine(ex.Message);
            if (ex is AggregateException)
            {
                var aex = (AggregateException) ex;
                foreach (var innerException in aex.Flatten().InnerExceptions)
                {
                    WriteExceptionMessages(output, innerException, 2);
                }
            }
            else if (ex.InnerException != null)
            {
                WriteExceptionMessages(output, ex.InnerException, 0);                
            }
            output.WriteLine();
            output.WriteLine(ex);

            output.WriteLine("-- App details --");
            var diagnosticInformation = Application.Current as IShareDiagnosticInformation;
            if (diagnosticInformation != null && diagnosticInformation.DiagnosticInformation != null)
            {
                output.WriteLine(diagnosticInformation.DiagnosticInformation);
            }

            output.WriteLine("-- Other details --");
            output.WriteLine("Time: {0}", DateTime.Now.ToUniversalTime().ToString("r"));
            output.WriteLine("Culture: {0}", CultureInfo.CurrentCulture);
            output.WriteLine("OS  Version: {0}", Environment.OSVersion);
            output.WriteLine("CLR Version: {0}", Environment.Version);
            output.WriteLine("Device Type: {0}", Microsoft.Devices.Environment.DeviceType);

            try
            {
                using (var appStorage = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    output.WriteLine("FreeSpace: {0:f3} MB", appStorage.AvailableFreeSpace / 1024f / 1024f);
                }
            }
            catch
            {
            }
        }

        static void WriteExceptionMessages(TextWriter output, Exception innerException, int indent)
        {
            var padLeft = innerException.Message.PadLeft(indent + innerException.Message.Length);
            output.WriteLine(padLeft);
            if (innerException.InnerException != null)
                WriteExceptionMessages(output, innerException.InnerException, indent + 2);
        }

        public static void ReportPreviousException()
        {
            try
            {
                var ignoreAll = false;
                string contents = null;
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (store.FileExists(Filename))
                    {
                        using (TextReader reader = new StreamReader(store.OpenFile(Filename, FileMode.Open, FileAccess.Read, FileShare.None)))
                        {
                            contents = reader.ReadToEnd();
                        }
                        SafeDeleteFile(store);
                    }
                    if (store.FileExists(NeverFilename))
                    {
                        ignoreAll = true;
                        Debug.WriteLine("This user has requested to not be bugged in this manner.");
                    }
                }
                if (contents != null && !ignoreAll)
                {
                    ReportError(contents);
                }
            }
            catch (Exception)
            {
            }
            finally
            {
                SafeDeleteFile(IsolatedStorageFile.GetUserStoreForApplication());
            }
        }

        static void ReportError(string contents)
        {
            var appInfo = Application.Current as IAppInfo;
            var diag = Application.Current as IShareDiagnosticInformation;

            if (appInfo == null) return;
            if (diag == null) return;

            if (Debugger.IsAttached)
            {
                DeveloperErrorDialog(contents, diag, appInfo);
            }
            else
            {
                FriendlyErrorDialog(contents, diag, appInfo);
            }
        }

        static void DeveloperErrorDialog(string contents, IShareDiagnosticInformation diag, IAppInfo appInfo)
        {
            var child = new Grid
                            {
                                Background = (Brush)Application.Current.Resources["PhoneBackgroundBrush"]
                            };
            child.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
            child.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
            child.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });

            var title = new TextBlock
                            {
                                Text = "Error Occured (Developer mode)",
                                Style = (Style)Application.Current.Resources["PhoneTextTitle2Style"]
                            };
            child.Children.Add(title);
            var richTextBox = new ScrollViewer
                                  {
                                      Content = new TextBlock
                                                    {
                                                        Text = contents,
                                                        Style = (Style)Application.Current.Resources["PhoneTextNormalStyle"]
                                                    },
                                      HorizontalScrollBarVisibility = ScrollBarVisibility.Auto
                                  };
            Grid.SetRow(richTextBox, 1);
            child.Children.Add(richTextBox);

            var emailButton = new Button { Content = "Email" };
            var closeButton = new Button { Content = "Close" };
            var sp = new StackPanel
                     {
                         HorizontalAlignment = HorizontalAlignment.Center,
                         Orientation = Orientation.Horizontal,
                         Children = { emailButton, closeButton }
                     };
            Grid.SetRow(sp, 2);
            child.Children.Add(sp);

            var app = Application.Current as PhoenixApplication;

            var height = 800;
            if (app != null && app.Shell != null && app.Shell.ApplicationBar != null)
                height -= 72;

            var size = Application.Current.RootVisual.RenderSize;
            child.Width = size.Width;
            child.Height = height - 32;
            var popup = new Popup
            {
                HorizontalAlignment = HorizontalAlignment.Stretch,
                VerticalAlignment = VerticalAlignment.Stretch,
                VerticalOffset = 32, //For AppBar
                HorizontalOffset = 0,
                Width = size.Width,
                Height = height,
                Child = child
            };

            closeButton.Click += (sender, args) => popup.IsOpen = false;
            emailButton.Click += (sender, args) =>
                                 {
                                     EmailError(contents, diag, appInfo);
                                     popup.IsOpen = false;
                                 };
            popup.IsOpen = true;
        }

        static void FriendlyErrorDialog(string contents, IShareDiagnosticInformation diag, IAppInfo appInfo)
        {
            var messageBoxResult = MessageBox.Show("Will you help improve the app by sending the error to the developer?", "Error Occured", MessageBoxButton.OKCancel);
            if (messageBoxResult == MessageBoxResult.OK)
            {
                EmailError(contents, diag, appInfo);
            }
            else
            {
                var disableResult = MessageBox.Show("Would you like to disable error reporting?", "Disable error reporting?", MessageBoxButton.OKCancel);
                if (disableResult == MessageBoxResult.OK)
                    DisableErrorReporting();
            }
        }

        static void EmailError(string contents, IShareDiagnosticInformation diag, IAppInfo appInfo)
        {
            var email = new EmailComposeTask
                        {
                            To = diag.DeveloperEmailAddress,
                            Subject = string.Format("Bug Report for {0}", appInfo.Title),
                            Body = contents
                        };
            email.Show();
        }

        public static void DisableErrorReporting()
        {
            try
            {
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (!store.FileExists(NeverFilename))
                        store.CreateFile(NeverFilename);
                }
            }
            catch (Exception)
            {
            }
        }

        public static void EnableErrorReporting()
        {
            try
            {
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (store.FileExists(NeverFilename))
                        store.DeleteFile(NeverFilename);
                }
            }
            catch (Exception)
            {
            }
        }

        private static void SafeDeleteFile(IsolatedStorageFile store)
        {
            try
            {
                store.DeleteFile(Filename);
            }
            catch (Exception)
            {
            }
        }
    }
    // ReSharper restore EmptyGeneralCatchClause
}